package com.yygo.jobs;

import com.yygo.model.*;
import com.yygo.model.enums.IDType;
import com.yygo.model.enums.Phase;
import com.yygo.model.enums.RequestStatus;
import com.yygo.service.LoanRequestManager;
import com.yygo.service.ProvinceManager;
import com.yygo.util.IdCardValidator;
import com.yygo.util.MobileFromUtil;
import com.yygo.util.StringUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.scheduling.quartz.QuartzJobBean;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/**
 * Created by tiansha on 2015/6/7.
 */
//@DisallowConcurrentExecution
public class RuleJob /*extends QuartzJobBean*/ {
    private static Log log = LogFactory.getLog(RuleJob.class);

    @Autowired
    private ProvinceManager provinceManager;
    @Autowired
    private LoanRequestManager loanRequestManager;
    @Autowired
    private List<RuleProcessor> ruleProcessors;

    public void setProvinceManager(ProvinceManager provinceManager) {
        this.provinceManager = provinceManager;
    }

    public void setLoanRequestManager(LoanRequestManager loanRequestManager) {
        this.loanRequestManager = loanRequestManager;
    }

    public void setRuleProcessors(List<RuleProcessor> ruleProcessors) {
        this.ruleProcessors = ruleProcessors;
    }

    /*@Override
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
        try {
            LoanRequest loanRequest = loanRequestManager.getNextForRuleProcessing();
            while (loanRequest != null) {

                processLoanRequest(loanRequest);


                loanRequest = loanRequestManager.getNextForRuleProcessing();
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }

    }*/

    public void processLoanRequest(LoanRequest loanRequest) throws InterruptedException, java.util.concurrent.ExecutionException {
        // process mobile location and identity no
        processMobileLocationAndIdentity(loanRequest, provinceManager);
        if (log.isDebugEnabled()) {
            log.debug("Processing rule check for " + loanRequest.getNumber());
        }
        List<Rule> rules = new ArrayList<>();
                /*log.debug("single thread start.");
                for (RuleProcessor ruleProcessor : ruleProcessors) {
                    String ruleName = ruleProcessor.getClass().getName();
                    if (log.isDebugEnabled()) {
                        log.debug("Start rule " + ruleName);
                    }
                    Rule rule = ruleProcessor.process(loanRequest);
                    if (log.isDebugEnabled()) {
                        log.debug("End rule " + ruleName + ", result: " + rule);
                    }
                    if (rule != null) {
                        rules.add(rule);
                    }
                }
                log.debug("single thread end.");*/
        log.debug("process rules start.");
        ExecutorService pool = Executors.newFixedThreadPool(3);
        List<Future<Rule>>  results = null;
        List<RuleTaskWithResult> taskList = new ArrayList<>();
        for (RuleProcessor ruleProcessor : ruleProcessors) {
            taskList.add(new RuleTaskWithResult(ruleProcessor, loanRequest));
        }
        try {
            results = pool.invokeAll(taskList);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        pool.shutdown();

        for(Future<Rule> future : results){
            Object object = future.get();
            if (object != null) {
                rules.add((Rule)object);
            }
        }
        Set<DealerRule> dealerRules = loanRequest.getDealer().getDealerRules();
        for(DealerRule dealerRule: dealerRules){
            if (dealerRule.isStatus()) {
                Rule rule = new Rule();
                rule.setCode(dealerRule.getCode());
                rule.setDescription(dealerRule.getDescription());
                rule.setLoanRequest(loanRequest);
                rules.add(rule);
            }
        }
        log.debug("process rules end.");
        if (rules.size() > 0) {
            List<String> codes = new ArrayList<>();
            for (Rule rule : rules) {
                rule.setLoanRequest(loanRequest);
                codes.add(rule.getCode());
            }
            if (log.isDebugEnabled()) {
                log.debug("There are " + rules.size() + " rules detected: " + codes.toString());
            }
        }

        loanRequest.getRules().clear();
        loanRequest.getRules().addAll(rules);
        loanRequest.setStatus(RequestStatus.request_audit);
        loanRequest.setPhase(Phase.cc_audit);
        loanRequestManager.saveLoanRequest(loanRequest);

        if (log.isDebugEnabled()) {
            log.debug("End rule check for " + loanRequest.getNumber());
        }
    }

    /*private static final String APPLICATION_CONTEXT_KEY = "applicationContext";
    private ApplicationContext getApplicationContext(JobExecutionContext context) throws Exception {
        ApplicationContext appCtx = null;
        appCtx = (ApplicationContext) context.getScheduler().getContext().get(APPLICATION_CONTEXT_KEY);
        if (appCtx == null) {
            throw new JobExecutionException("No application context available in scheduler context for key \"" + APPLICATION_CONTEXT_KEY + "\"");
        }
        return appCtx;
    }*/

    class RuleTaskWithResult implements Callable<Rule> {
        RuleProcessor ruleProcessor;
        LoanRequest loanRequest;
        public RuleTaskWithResult(RuleProcessor ruleProcessor, LoanRequest loanRequest) {
            this.ruleProcessor = ruleProcessor;
            this.loanRequest = loanRequest;
        }

        public Rule call() {
            return ruleProcessor.process(loanRequest);
        }
    }

    private void processMobileLocationAndIdentity(LoanRequest loanRequest, ProvinceManager provinceManager) {
        LoanPerson requestPerson = loanRequest.getRequestPerson();
        setIdentity(requestPerson, provinceManager);
        setMobileLocation(loanRequest.getRequestPerson(), provinceManager);
        LoanPerson coPerson = loanRequest.getCoPerson();
        if (coPerson != null) {
            setMobileLocation(coPerson, provinceManager);
            setIdentity(coPerson, provinceManager);
        }
        LoanPerson dbPerson = loanRequest.getDbPerson();
        if (dbPerson != null) {
            setMobileLocation(dbPerson, provinceManager);
            setIdentity(dbPerson, provinceManager);
        }
    }

    private void setMobileLocation(LoanPerson loanPerson, ProvinceManager provinceManager) {
        String mobileNo = loanPerson.getMobileNo();
        if (!StringUtil.isBlankOrNull(mobileNo)) {
            String from = MobileFromUtil.getMobileAddress(mobileNo);
            List<Province> provinces = provinceManager.getAll();
            for (Province province : provinces) {
                String provinceName = province.getName();
                if (from.indexOf(provinceName) != -1) {
                    loanPerson.setMobileProvince(provinceName);
                    List<City> cities = provinceManager.getMyCity(provinceName);
                    for (City city : cities) {
                        if (from.indexOf(city.getName()) != -1) {
                            loanPerson.setMobileCity(city.getName());
                            break;
                        }
                    }
                    break;
                }
            }
        }
    }

    private void setIdentity(LoanPerson loanPerson, ProvinceManager provinceManager) {
        if (IDType.Identity.equals(loanPerson.getIdType()) && !StringUtil.isBlankOrNull(loanPerson.getIdNo())) {
            String code = IdCardValidator.getProvinceCode(loanPerson.getIdNo());
            String provinceName = provinceManager.getProvinceNameByCode(code);
            loanPerson.setIdProvince(provinceName);
        }
    }

}
